#include "RtspMessage.h"


namespace jlh
{
	uint8_t RtspRequest::GetRtpChannel() const
	{
		auto itr = header_line_param_.find("rtp_channel");
		if (header_line_param_.end() != itr)
			return itr->second.second;

		return 0;
	}
	uint8_t RtspRequest::GetRtcpChannel() const
	{
		auto itr = header_line_param_.find("rtcp_channel");
		if (header_line_param_.end() != itr)
			return itr->second.second;

		return 0;
	}
	uint16_t RtspRequest::GetRtpPort() const
	{
		auto itr = header_line_param_.find("rtp_port");
		if (header_line_param_.end() != itr)
			return itr->second.second;

		return 0;
	}
	uint16_t RtspRequest::GetRtcpPort() const
	{
		auto itr = header_line_param_.find("rtcp_port");
		if (header_line_param_.end() != itr)
			return itr->second.second;

		return 0;
	}

	bool RtspRequest::ParseRequest(AudioBuffer& buffer)
	{
		if (buffer.peek()[0] == '$') {
			method_ = RTCP;
			return true;
		}

		bool bRet = true;
		while (1)
		{
			if (kParseRequestLine == state_)
			{
				const char* firstCrlf = buffer.FindFirstCrlf();
				if (nullptr != firstCrlf)
				{
					bRet = ParseRequestLine(buffer.peek(), firstCrlf);
					buffer.retrieveUntil(firstCrlf + 2);
				}
				if (kParseHeaderLine == state_)
					continue;
				else
				{
					break;
				}
			}
			else if (kParseHeaderLine == state_)
			{
				const char* lastCrlf = buffer.FindLastCrlf();
				if (nullptr != lastCrlf)
				{
					bRet = ParseHeadersLine(buffer.peek(), lastCrlf);
					buffer.retrieveUntil(lastCrlf + 2);
				}
				break;
			}
			else if (kGotAll == state_)
			{
				buffer.retrieveAll();
				return true;
			}
		}

		return bRet;
	}

	bool RtspRequest::ParseHeadersLine(const char* begin, const char* end)
	{
		std::string message(begin, end);
		if (!ParseCSeq(message)) {
			if (header_line_param_.end() == header_line_param_.find("cseq")) {
				return false;
			}
		}

		if ((DESCRIBE == method_) || (SETUP == method_) || (PLAY == method_))
		{
			ParseAuthorization(message);
		}

		if (method_ == OPTIONS) {
			state_ = kGotAll;
			return true;
		}

		if (DESCRIBE == method_) {
			if (ParseAccept(message)) {
				state_ = kGotAll;
			}
			return true;
		}

		if (SETUP == method_) {
			if (ParseTransport(message)) {
				ParseMediaChannel(message);
				state_ = kGotAll;
			}
			return true;
		}

		if (PLAY == method_) {
			if (ParseSessionId(message)) {
				state_ = kGotAll;
			}
			return true;
		}

		if (TEARDOWN == method_) {
			state_ = kGotAll;
			return true;
		}

		if (GET_PARAMETER == method_) {
			state_ = kGotAll;
			return true;
		}

		return true;
	}

	int RtspRequest::BuildGetParameterRes(char* buf, int buf_size, uint32_t session_id)
	{
		memset(buf, 0, buf_size);
		sprintf_s(buf, buf_size,
			"RTSP/1.0 200 OK\r\n"
			"CSeq: %u\r\n"
			"Session: %u\r\n"
			"\r\n",
			this->GetCSeq(),
			session_id);
		return strlen(buf);
	}

	int RtspRequest::BuildTeardownRes(char* buf, int buf_size, uint32_t session_id)
	{
		memset(buf, 0, buf_size);
		sprintf_s(buf, buf_size,
			"RTSP/1.0 200 OK\r\n"
			"CSeq: %u\r\n"
			"Session: %u\r\n"
			"\r\n",
			this->GetCSeq(),
			session_id);
		return strlen(buf);
	}

	bool RtspRequest::ParseSessionId(std::string& message)
	{
		std::size_t pos = message.find("Session");
		if (std::string::npos != pos)
		{
			uint32_t session_id = 0;
			if (1 != sscanf(message.c_str() + pos, "%*[^:]: %u", &session_id)) {
				return false;
			}
			return true;
		}
		return false;
	}

	bool RtspRequest::ParseMediaChannel(std::string& message)
	{
		channel_id_ = channel_0;
		auto itr = request_line_param_.find("url");
		if (request_line_param_.end() != itr) {
			std::size_t pos = itr->second.first.find("track1");
			if (std::string::npos != pos) {
				channel_id_ = channel_1;
			}
		}

		return true;
	}

	bool RtspRequest::ParseTransport(std::string& message)
	{
		std::size_t pos = message.find("Transport");
		if (std::string::npos != pos)
		{
			if (std::string::npos != (pos = message.find("RTP/AVP/TCP")))
			{
				transport_ = RTP_OVER_TCP;
				uint16_t rtpChannel, rtcpChannel;
				if (2 != sscanf(message.c_str() + pos, "%*[^;];%*[^;];%*[^=]=%hu-%hu", &rtpChannel, &rtcpChannel)) {
					return false;
				}

				header_line_param_.emplace("rtp_channel", std::make_pair("", rtpChannel));
				header_line_param_.emplace("rtcp_channel", std::make_pair("", rtcpChannel));
			}
			else if (std::string::npos != (pos = message.find("RTP/AVP")))
			{
				uint16_t rtp_port=0, rtcp_port = 0;
				if (std::string::npos != (message.find("unicast", pos))) {
					transport_ = RTP_OVER_UDP;
					if (2 != sscanf(message.c_str() + pos, "%*[^;];%*[^;];%*[^=]=%hu-%hu", &rtp_port, &rtcp_port)) {
						return false;
					}
				}
				else if (std::string::npos != message.find("multicast", pos)) {
					transport_ = RTP_OVER_MULTICAST;
				}
				else {
					return false;
				}

				header_line_param_.emplace("rtp_port", std::make_pair("", rtp_port));
				header_line_param_.emplace("rtcp_port", std::make_pair("", rtcp_port));
			}
			else
			{
				return false;
			}

			return true;
		}

		return false;
	}

	bool RtspRequest::ParseAccept(std::string& message)
	{
		if ((std::string::npos == message.rfind("Accept"))
			|| (std::string::npos == message.rfind("sdp"))) {
			return false;
		}
		return true;
	}

	bool RtspRequest::ParseAuthorization(std::string& message)
	{
		std::size_t pos = message.find("Authorization");
		if (std::string::npos != pos)
		{
			if (std::string::npos != (pos = message.find("response=")))
			{
				auth_response_ = message.substr(pos + 10, 32);
				if (32 == auth_response_.size()) {
					return true;
				}
			}
		}

		auth_response_.clear();
		return false;
	}

	bool RtspRequest::ParseCSeq(std::string& message)
	{
		std::size_t pos = message.find("CSeq");
		if (std::string::npos != pos)
		{
			uint32_t cseq = 0;
			sscanf(message.c_str() + pos, "%*[^:]: %u", &cseq);
			header_line_param_.emplace("cseq", std::make_pair("", cseq));
			return true;
		}

		return false;
	}

	bool RtspRequest::ParseRequestLine(const char* begin, const char* end)
	{
		std::string message(begin, end);
		char method[64] = { 0 };
		char url[512] = { 0 };
		char version[64] = { 0 };

		if (3 != sscanf(message.c_str(), "%s %s %s", method, url, version))
		{
			return true;
		}

		std::string method_str(method);
		if (method_str == "OPTIONS") {
			method_ = OPTIONS;
		}
		else if (method_str == "DESCRIBE") {
			method_ = DESCRIBE;
		}
		else if (method_str == "SETUP") {
			method_ = SETUP;
		}
		else if (method_str == "PLAY") {
			method_ = PLAY;
		}
		else if (method_str == "TEARDOWN") {
			method_ = TEARDOWN;
		}
		else if (method_str == "GET_PARAMETER") {
			method_ = GET_PARAMETER;
		}
		else {
			method_ = NONE;
			return false;
		}

		if (0 != strncmp(url, "rtsp://", 7)) {
			return false;
		}

		uint16_t port = 0;
		char ip[64] = { 0 };
		char suffix[64] = { 0 };
		if (3 == sscanf(url + 7, "%[^:]:%hu/%s", ip, &port, suffix)) {

		}
		else if (2 == sscanf(url + 7, "%[^/]/%s", ip, suffix)) {
			port = 554;
		}
		else {
			return false;
		}

		request_line_param_.emplace("url", std::make_pair(std::string(url), 0));
		request_line_param_.emplace("url_ip", std::make_pair(std::string(ip), 0));
		request_line_param_.emplace("url_port", std::make_pair("", (uint32_t)port));
		request_line_param_.emplace("url_suffix", std::make_pair(std::string(suffix), 0));
		request_line_param_.emplace("version", std::make_pair(std::string(version), 0));
		request_line_param_.emplace("method", std::make_pair(std::move(method_str), 0));
		
		state_ = kParseHeaderLine;
		return true;
	}

	std::string RtspRequest::GetRtspUrlSuffix()
	{
		auto itr = request_line_param_.find("url_suffix");
		if (request_line_param_.end() != itr) {
			return itr->second.first;
		}
		return "";
	}

	uint32_t RtspRequest::GetCSeq() const
	{
		uint32_t cseq = 0;
		auto iter = header_line_param_.find("cseq");
		if (header_line_param_.end() != iter)
			cseq = iter->second.second;
		return cseq;
	}

	int RtspRequest::BuildPlayRes(char* buf, int buf_size, const char* rtpInfo, uint32_t session_id)
	{
		memset(buf, 0, buf_size);
		sprintf_s(buf, buf_size,
			"RTSP/1.0 200 OK\r\n"
			"CSeq: %u\r\n"
			"Range: npt=0.000-\r\n"
			"Session: %u; timeout=60\r\n\r\n",
			this->GetCSeq(),
			session_id);
		return strlen(buf);
	}

	int RtspRequest::BuildUnsupportedRes(char* buf, int buf_size)
	{
		memset(buf, 0, buf_size);
		sprintf_s(buf, buf_size,
			"RTSP/1.0 461 Unsupported transport\r\n"
			"CSeq: %u\r\n"
			"\r\n",	//add
			this->GetCSeq());
		return strlen(buf);
	}

	int RtspRequest::BuildSetupUdpRes(char* buf, int buf_size, uint16_t rtp_chn, uint16_t rtcp_chn, uint32_t session_id)
	{
		memset(buf, 0, buf_size);
		sprintf_s(buf, buf_size,
			"RTSP/1.0 200 OK\r\n"
			"CSeq: %u\r\n"
			"Transport: RTP/AVP;unicast;client_port=%hu-%hu;server_port=%hu-%hu\r\n"
			"Session: %u\r\n"
			"\r\n"
			, this->GetCSeq(),
			this->GetRtpPort(),
			this->GetRtcpPort(),
			rtp_chn,
			rtcp_chn,
			session_id);
		return strlen(buf);
	}

	int RtspRequest::BuildSetupTcpRes(char* buf, int buf_size, uint16_t rtp_chn, uint16_t rtcp_chn, uint32_t session_id)
	{
		memset(buf, 0, buf_size);
		sprintf_s(buf, buf_size,
			"RTSP/1.0 200 OK\r\n"
			"CSeq: %u\r\n"
			"Transport: RTP/AVP/TCP;unicast;interleaved=%d-%d\r\n"
			"Session: %u\r\n"
			"\r\n",
			this->GetCSeq(),
			rtp_chn, rtcp_chn,
			session_id);
		return strlen(buf);
	}

	int RtspRequest::BuildDescribeRes(char* buf, int buf_size, const char* sdp)
	{
		memset(buf, 0, buf_size);
		sprintf_s(buf, buf_size,
			"RTSP/1.0 200 OK\r\n"
			"CSeq: %u\r\n"
			"Content-Length: %d\r\n"
			"Content-Type: application/sdp\r\n"
			"\r\n"
			"%s",
			this->GetCSeq(),
			(int)strlen(sdp),
			sdp);
		return strlen(buf);
	}

	int RtspRequest::BuildServerErrorRes(char* buf, int buf_size)
	{
		memset(buf, 0, buf_size);
		sprintf_s(buf, buf_size, "RTSP/1.0 500 Internal Server Error\r\n"
			"CSeq: %u\r\n"
			"\r\n",
			this->GetCSeq());
		return strlen(buf);
	}

	int RtspRequest::BuildNotFoundRes(char* buf, int buf_size)
	{
		memset(buf, 0, buf_size);
		sprintf_s(buf, buf_size, "RTSP/1.0 404 Stream Not Found\r\n"
			"CSeq: %u\r\n"
			"\r\n",
			this->GetCSeq());
		return strlen(buf);
	}

	int RtspRequest::BuildOptionRes(char* buf, int buf_size)
	{
		memset(buf, 0, buf_size);
		sprintf_s(buf, 2048, "RTSP/1.0 200 OK\r\n"
			"CSeq: %u\r\n"
			"Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY\r\n"
			"\r\n",
			this->GetCSeq());
		return strlen(buf);
	}




	bool RtspResponse::ParseResponse(AudioBuffer& buffer)
	{
		if (nullptr != strstr(buffer.peek(), "\r\n\r\n"))
		{
			if (nullptr == strstr(buffer.peek(), "OK"))
			{
				return false;
			}

			char* ptr = strstr(buffer.peek(), "Session");
			if (nullptr != ptr)
			{
				char session_id[50] = { 0 };
				if (1 == sscanf(ptr, "%*[^:]: %s", session_id))
					session_ = session_id;
			}

			cseq_++;
			buffer.retrieveUntil("\r\n\r\n");
		}
		return true;
	}

	int RtspResponse::BuildOptionReq(char* buf, int buf_size)
	{
		memset(buf, 0, buf_size);
		sprintf_s(buf, buf_size,
			"OPTIONS %s RTSP/1.0\r\n"
			"CSeq: %u\r\n"
			"User-Agent: %s\r\n"
			"\r\n",
			rtsp_url_.c_str(),
			this->GetCSeq() + 1,
			user_agent_.c_str());
		method_ = OPTIONS;
		return strlen(buf);
	}

	int RtspResponse::BuildAnnounceReq(char* buf, int buf_size, const char* sdp)
	{
		memset(buf, 0, buf_size);
		sprintf_s(buf, buf_size,
			"ANNOUNCE %s RTSP/1.0\r\n"
			"Content-Type: application/sdp\r\n"
			"CSeq: %u\r\n"
			"User-Agent: %s\r\n"
			"Session: %s\r\n"
			"Content-Length: %d\r\n"
			"\r\n"
			"%s",
			rtsp_url_.c_str(),
			this->GetCSeq() + 1,
			user_agent_.c_str(),
			this->GetSession().c_str(),
			(int)strlen(sdp),
			sdp);

		method_ = ANNOUNCE;
		return (int)strlen(buf);
	}

	int RtspResponse::BuildSetupTcpReq(char* buf, int buf_size, int trackId)
	{
		int interleaved[2] = { 0,1 };
		if (1 == trackId) {
			interleaved[0] = 2;
			interleaved[1] = 3;
		}

		memset(buf, 0, buf_size);
		sprintf_s(buf, buf_size,
			"SETUP %s/track%d RTSP/1.0\r\n"
			"Transport: RTP/AVP/TCP;unicast;mode=record;interleaved=%d-%d\r\n"
			"CSeq: %u\r\n"
			"User-Agent: %s\r\n"
			"Session: %s\r\n"
			"\r\n"
			, rtsp_url_.c_str(),
			trackId,
			interleaved[0],
			interleaved[1],
			this->GetCSeq() + 1,
			user_agent_.c_str(),
			this->GetSession().c_str());
		method_ = SETUP;
		return (int)strlen(buf);
	}

	int RtspResponse::BuildRecordReq(char* buf, int buf_size)
	{
		memset(buf, 0, buf_size);
		sprintf_s(buf, buf_size,
			"RECORD %s RTSP/1.0\r\n"
			"Range: npt=0.000-\r\n"
			"CSeq: %u\r\n"
			"User-Agent: %s\r\n"
			"Session: %s\r\n"
			"\r\n",
			rtsp_url_.c_str(),
			this->GetCSeq() + 1,
			user_agent_.c_str(),
			this->GetSession().c_str());
		method_ = RECORD;
		return (int)strlen(buf);
	}

}